#include <iostream>
#include "Socket.hpp"
#include <sys/epoll.h>
#include <memory>
#include <algorithm>
#include "Comm.hpp"
using namespace LogModule;
#define NUM 64

class EpollServer
{
    const static int revs_num = 64;
public:
    EpollServer(int port)
    :_listen_socket(std::make_unique<TcpSocket>(port)), _isrunning(false),_epfd(-1)
    {
    }
    ~EpollServer()
    {
        _listen_socket->Close();
    }
    void Init()
    {
        _listen_socket->BuildTcpSocket();
        _epfd = epoll_create(256);
        if(_epfd<0)
        {
            LOG(LogLevel::ERROR)<<"epoll_create error";
            exit(EPOLL_CREATE_ERR);
        }
        //先将LIstensockfd设置进红黑树
        struct epoll_event ev;
        ev.events = EPOLLIN;
        //为什么要设置data中的fd？？？
        ev.data.fd = _listen_socket->Fd();
        int ret = epoll_ctl(_epfd,EPOLL_CTL_ADD,_listen_socket->Fd(),&ev);
        if(ret<0)
        {
            LOG(LogLevel::ERROR)<<"epoll_ctl error";
            exit(EPOLL_CTL_ERR);
        }
    }
    void Start()
    {
        _isrunning = true;
        //
        while (_isrunning)
        {

            //阻塞式
            int n = epoll_wait(_epfd,_revs,revs_num,-1);
            if (n > 0)
            {
                LOG(LogLevel::DEBUG) << "time out: ";
                Dispatcher(n);
            }
            else if (n == 0)
            {
                LOG(LogLevel::DEBUG) << "time out....";
            }
            else
            {
                LOG(LogLevel::ERROR) << "epoll error";
                return;
            }

            // InetAddr client;
            // int newsockfd = _listen_socket->Accept(&client);
            // LOG(LogLevel::DEBUG)<<"获得新连接: "<<newsockfd<<" "<<client.Ip()<<" "<<client.Port();
        }
        _isrunning = false;
    }
    void Dispatcher(int num)
    {
        for (int i = 0; i < num; i++)
        {
            //在队列中的都是已就绪的事件
            if (_revs[i].data.fd == _listen_socket->Fd())
            {
                //这就是为什么要把fd设置进epoll_event的data中的原因，可以取出
                if (_revs[i].data.fd&EPOLLIN)
                {
                    Accepter();
                }
            }
            else
            {
                if (_revs[i].data.fd&EPOLLIN)
                {
                    Recv(i);
                }
                // if(_revs[i].data.fd&POLLOUT)
                // {}
            }
        }
    }

    void Recv(int who)
    {
        char buffer[1024];
        int n = recv(_revs[who].data.fd, buffer, sizeof(buffer) - 1, 0);
        if (n > 0)
        {
            buffer[n] = 0;
            std::cout << "client# " << buffer << std::endl;
            std::string message = "echo# ";
            message += buffer;
            send(_revs[who].data.fd, message.c_str(), message.size(), 0);
        }
        else if (n == 0)
        {
            LOG(LogLevel::DEBUG) << "客户端退出," << _revs[who].data.fd;
            //先移除队列中的节点，因为移除队列中的节点时，其fd必须是合法的
            //所以先移除，在关闭fd
            int ret = epoll_ctl(_epfd,EPOLL_CTL_DEL,_revs[who].data.fd,&_revs[who]);
            if(ret<0)
            {
                LOG(LogLevel::ERROR)<<"epoll_ctl del error";
                exit(EPOLL_CTL_ERR);
            }
            close(_revs[who].data.fd);
        }
        else
        {
            LOG(LogLevel::DEBUG) << "客户端退出," << _revs[who].data.fd;
            //先移除队列中的节点，因为移除队列中的节点时，其fd必须是合法的
            //所以先移除，在关闭fd
            int ret = epoll_ctl(_epfd,EPOLL_CTL_DEL,_revs[who].data.fd,&_revs[who]);
            if(ret<0)
            {
                LOG(LogLevel::ERROR)<<"epoll_ctl del error";
                exit(EPOLL_CTL_ERR);
            }
            close(_revs[who].data.fd);
        }
    }
    void Accepter()
    {
        InetAddr client;
        int newsockfd = _listen_socket->Accept(&client);
        LOG(LogLevel::DEBUG) << "获得新连接: " << newsockfd << " " << client.Ip() << " " << client.Port();
        // recv()?就一定有数据吗？
        struct epoll_event ev;
        ev.data.fd = newsockfd;
        ev.events = EPOLLIN;
        int ret = epoll_ctl(_epfd,EPOLL_CTL_ADD,newsockfd,&ev);
        if(ret<0)
        {
            LOG(LogLevel::ERROR)<<"epoll_ctl error";
            exit(EPOLL_CTL_ERR);
        }
    }
private:
    std::unique_ptr<Socket> _listen_socket;
    bool _isrunning;
    int _epfd;
    struct epoll_event _revs[revs_num];
};